package com.sfac.springboot.springboot.modules.common.controller;

import com.sfac.springboot.springboot.modules.common.entity.ExceptionLog;
import com.sfac.springboot.springboot.modules.common.service.ExceptionLogService;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.UnauthorizedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;

import javax.servlet.http.HttpServletRequest;

/*
* 捕获所有控制器所有异常，并进行处理
* */
@ControllerAdvice
public class ExceptionController {

    public final static Logger LOGGER = LoggerFactory.getLogger(ExceptionController.class);

    @Autowired
    private ExceptionLogService exceptionLogService;

    /*
    * 处理控制层所有的异常，根据 调用类型 和 返回类型 来决定返回的对象。根据调用类型来决定返回对象
    * 接口调用异常，返回403、404、500对应的Json数据
    * 页面调用异常，返回403/404/500对应的错误界面*/
    @ExceptionHandler(value = Exception.class)
    public ModelAndView handleException(
            HttpServletRequest request,Exception exception){
        //输入异常信息
        exception.printStackTrace();
        LOGGER.debug(exception.getMessage());

        //插入异常信息
        insertExceptionLog(request,exception);

        //根据异常类型、封装数据(状态码、消息、异常错误页面对应的路径)
        int status = 200;
        String message = "";
        String data = "";
        //暂将 ArithmeticException作为没有权限异常处理
        if (exception instanceof UnauthorizedException){
            status = 403;
            message = "没有权限";
            data = "/common/403";
        }else {
            status = 500;
            message = "内部错误";
            data = "/common/500";
        }

        //判断请求类型，接口调用返回json数据，反之没有权限
        if (checkCallType(request)){
            ModelAndView mv = new ModelAndView(new MappingJackson2JsonView());
            mv.addObject("status",status);
            mv.addObject("message",message);
            mv.addObject("data",data);
            return mv;
        }else {
            return new ModelAndView(new RedirectView(data));
        }
    }

    private boolean checkCallType(HttpServletRequest request){
        HandlerMethod handlerMethod =(HandlerMethod) request.getAttribute(
                "org.springframework.web.servlet.HandlerMapping.bestMatchingHandler");
        //从方法上面获取ResponseBody注解
        ResponseBody[] annotations1 = handlerMethod.getMethod().getAnnotationsByType(ResponseBody.class);
        //从类上面获取ResponseBody注解
        ResponseBody[] annotations2 =handlerMethod.getBeanType().getAnnotationsByType(ResponseBody.class);
        //从类上面获取RestController注解
        RestController[] annotations3 =handlerMethod.getBeanType().getAnnotationsByType(RestController.class);
        return (annotations1.length > 0) || (annotations2.length > 0) || (annotations3.length > 0);
    }

    //保存异常信息
    private void insertExceptionLog(
            HttpServletRequest request,Exception exception){
        ExceptionLog exceptionLog = new ExceptionLog();
        exceptionLog.setIp(request.getRemoteAddr());
        exceptionLog.setPath(request.getServletPath());

        HandlerMethod handlerMethod =(HandlerMethod) request.getAttribute(
                "org.springframework.web.servlet.HandlerMapping.bestMatchingHandler");
        exceptionLog.setClassName(handlerMethod.getBeanType().getName());
        exceptionLog.setMethodName(handlerMethod.getMethod().getName());
        exceptionLog.setExceptionType(exception.getClass().getSimpleName());
        String message = exception.getMessage();
        int length = 0;
        if (StringUtils.isNotBlank(message)) {
            length = message.length() > 200 ? 200 : message.length();
        }
        exceptionLog.setExceptionMessage(StringUtils.isNoneBlank(exception.getMessage()) ?
                exception.getMessage().substring(0,200) : "");

        exceptionLogService.insertExceptionLog(exceptionLog);
    }
}
